
python define常數 在 コバにゃんチャンネル Youtube 的最佳貼文

Search
实际开发中我们经常会碰到需要定义常量的情形,c语言提供了const关键字来实现,但是python本身却没有提供const,要想在python代码中定义常量,需要我们 ... ... <看更多>
python define常數 2023-在Facebook/IG/Youtube上的焦點新聞和熱門話題資訊,找python def教學,python define常數,python函數查詢在2022年該注意什麼?python define ... ... <看更多>
#1. python中定义常量原创
后来,python提供了新的方法实现常量:即通过自定义类实现常量。这要求符合“命名全部为大写”和“值一旦被绑定便不可再修改”这两个条件。 用自定义 ...
常數 :在程式執行過程中,值不會發生變化的量。 無論是變數還是常數,在建立時都會在記憶體中開闢一塊空間,用於儲存它的值。 二 ...
#3. 內建常數
python 中没有使用语法强制定义常量,也就是说,python中定义常量本质上就是变量。如果非要定义常量,变量名必须全大写。 AGE_OF_NICK = 19 print( ...
Globals(全域變數), Constants(常數), 一律大寫, PI=3.14. function(函數), 一律小寫,文字串接的時候使用下底線, def add():. Class(類別), 單字開頭第一個字大寫, ...
常數 在建立時就指定其初始值,此值不能在程式中加以改變,例如『圓周率』;建立常數的方法有兩種:. 『#define』及『const』 ...
#7. python中定义常量
实际开发中我们经常会碰到需要定义常量的情形,c语言提供了const关键字来实现,但是python本身却没有提供const,要想在python代码中定义常量,需要我们 ...
字⾯常數就是直接寫進Python 程式原始碼的數值,依. 資料型態分類有. —字串字⾯常數(string literal). —字節字⾯常數(bytes literal). —整數字⾯常數(integer literal).
#9. 變數variable - Python 教學 - STEAM 教育學習網
如果變數的內容不要被修改( 其他程式語言稱為「常數」,也就是內容不會改變的數),變數名稱建議全部用大寫英文字母,例如MAX_NUMBER。 如果變數名稱包含專有名詞,例如 ...
#10. Python常量- 知乎
这篇文章主要介绍了Python中实现常量(Const)功能,python语言本身没有提供const,本文使用一个properties文件(类)来实现常量定义功能,并介绍了使用 ...
#11. Python 定义自己的常量类
在 Python 中,当我们对类的属性进行赋值时,会自动调用 object 类的 __setattr__() ... 方法 def __setattr__(self, name, value): if name in self.
#12. 定义常量变量的最佳方法是什么?
通常在Python中,常量是大写的(PEP 8标准),这有助于程序员知道它是一个常数。 Ex. MY_CONSTANT = "Whatever". 另一种有效的方式,我没有使用,但听 ...
#13. python 定义类常量
python 定义类常量技术、学习、经验文章掘金开发者社区搜索结果。掘金是一个帮助开发者成长的 ... const常量和#define宏定义常量区别 ... PHP常量的赋值必须是常数.
#14. python define常數2023-在Facebook/IG/Youtube上的焦點新聞 ...
python define常數 2023-在Facebook/IG/Youtube上的焦點新聞和熱門話題資訊,找python def教學,python define常數,python函數查詢在2022年該注意什麼?python define ...
#15. python — 怎麼在兩個檔案之間分享全域變數 - ss - Medium
一般使用global variable 的情境是這樣的 tutorial1 num = 1def increment(): global num num += 1. 如此以來, 即使不透過傳遞num值到函式裡, 我們也可以將num這個值做 ...
#16. Python - 維基百科,自由的百科全書
內建常數 True 、 False 和 None 於Python 3.0中成為關鍵字,關鍵字 nonlocal ... 類名字接著在原來的局部名字空間中,被繫結到這個類對象。 def 語句,是定義函數和 ...
#17. 競賽技巧
常數 宣告. 常數變數宣告時通常使用全部大寫 ... #define INF 0x3f3f3f3f ... TLE時除了考慮複雜度以外,常數也是很重要的記得一定要在每題程式碼開頭加上這兩行 ...
#18. Tool - C-define Parser - zhung to be lazy…
... 樣子,難不成我還要一個個去展開後的程式裡面翻? 因此就有了這個Python 寫的Tool 誕生。 ... 如果內容還存在#define 的常數,先展開內容的Macro
#19. PHP教學- 常數(Constants)
PHP教學- 常數(Constants) · 留言 · 這個網誌中的熱門文章 · c語言-關於#define用法 · python 研究-with as 用法 · PHP教學- 資料型態(Data Type) - 上 · 封存.
#20. 6. 命名約定 - Google 開源專案風格指南(繁體中文版)
最重要的一致性規則是命名管理. 命名風格快速獲知名字代表是什麼東東: 類型? 變數? 函式? 常數? 巨集... ? 甚至不需要去 ...
#21. 使用Python 在Redis 叢集上啟用傳輸中加密
首先,讓我們定義一些簡單的Python 字串常數,這些常數會保留建立ElastiCache 叢集所 ... #Class definitions class Config: def __init__( self, instance_type: str ...
#22. 第5 章- 產生追蹤緩衝區
ThreadX 事件追蹤支援; 啟用事件追蹤; 定義Time-Stamp常數; 匯出追蹤緩衝區 ... #ifndef TX_TRACE_TIME_SOURCE #define TX_TRACE_TIME_SOURCE ...
#23. 語法與型別- JavaScript - MDN Web Docs
本章討論JavaScript 的基本語法與基礎資料類型、包括變數、常數、字元常數. ... 基本語法借鑒自Java,C 或是C++,但亦受Awk、Perl 和Python 的影響。
#24. python的常量_python中如何定义常量
Python 并未提供如C/C++/Java一样的const修饰符,换言之,python中没有常量,python程序一般通过约定俗成的变量名全大写的形式 ... def __setattr__(self,name,value):.
#25. 1. 絕對值: visual 模組中abs可用於整數,浮點數,複數,向量 ...
def computeHCF(x,y): if x > y: smaller = y else: smaller = x for i in range(1, smaller+1): ... 首先編寫了一個儲存許多物理常數的python程式(constants.py)。
#26. C 速查手冊- 單元10 - 前置處理器
引入標頭檔, #include <stdio.h> ; 定義符號常數, #define PI 3.14 ; 定義巨集, #define CIRCUM(r) 2 * PI * (r) ...
#27. VHDL预编译(常数,generic…)
在VHDL语言中没有类似C语言中的#define语句定义宏来实现字符常量(用字符代替常数)。但VHDL语言中也有类似的机制实现常量的定义和使用, ...
#28. Python語句— Ren'Py 中文文件
Ren'Py使用Python程序語言編寫,並且支持在Ren'Py腳本中包含Python語句。 ... 通過define語句定義的變數會被當作一個常數,不會保存或讀取,也不該被修改。
#29. C 常量
使用#define 预处理器: #define 可以在程序中定义一个常量,它在编译时会被替换为其对应的值。 使用const 关键字:const 关键字用于声明一个只读变量,即该变量的值 ...
#30. C语言define 定义常量
预编译不是编译,而是编译前的处理。这个操作是在正式编译之前由系统自动完成的。 二.define实战. 举个栗子:语文/数学 ...
#31. Python程式語言part1
MS Visual Studio環境也支援Python ... 同是不能改變,可以作為dictionary的鍵、函式回傳、 ... 鍵必需是不可變的型態(如: 常數、常字串、Tuple).
#32. APCS 完全攻略:從新手到高手,Python 解題必備!(暢銷 ...
而程式設計觀念題的考試重點包括:程式設計基本觀念、輸出入指令、資料型態、常數與變數、全域及區域、流程控制、迴圈、函式、遞迴、陣列與矩陣、 ...
#33. c语言常量定义等号_抖抖音
2020-05-29 13:05:00 c语言常量定义等号推荐内容: 使用#define预处理器。 ... 六种流行语言(C、C++、Python、JavaScript、PHP、Java)对比 ... 符号常量+常数常量。
#34. push、top并能在常数时间内检测最小元素的栈
设计一个pop、push、top、getmin操作,并能在常数时间内检测最小元素的栈class Minstack(object): def init(self): self.stack = [] self.
#35. const、static、volatile 排列組合彙整(用於變數、陣列)
... 告訴編譯器這個變量是不允許被改變的,也就等價的變成一個常量(常數)。 ... 順道一提,還記得我們幾乎在每一個檔案中都有使用#define "studio.h" ...
#36. C語言基本資料型別與變數
Python - variable typesLearnbay Datascience59 views•10 slides ... 置處理指令定義符號常數,語法如下: #define 識別字常數值 #define 前置處理指令與常數的識別 ...
#37. C 語言初學教材- 第四章while (1) 目錄
通常#define 會放在#include 的下方,注意前置處理器結尾不需要分號; 。 然後我們回到擲骰子遊戲,使用SIZE 的常數,先來模擬勝負個SIZE 次吧! 單純模擬 ...
#38. Bit - 演算法筆記
... 負值(最左位元為1),實施右移運算,最左位元應該補0還是補1: Implementation-defined Behavior ... C/C++的常數,若帶小數點、若是科學標記法,預設是double。
#39. 常數定義的問題包括PTT、Dcard、Mobile01,我們都能挖掘 ...
超圖解Python 物聯網實作入門:使用ESP8266 與MicroPython · 常數定義進入發燒排行的影片 · More videos on YouTube · 鰭式場效電晶體寄生元件之模擬與改善 · SAP ABAP開發技術 ...
#40. Online Compiler and IDE >> C/C++, Java, PHP, Python, ...
What is Ideone? Ideone is an online compiler and debugging tool which allows you to compile source code and execute it online in more than 60 programming ...
#41. [Python] 值得參考的Coding Style 整理筆記
那麼,以下便是Google 開源專案風格指南中所定義的Python 風格規範: ... No class x: def __init__(self): ... def process(self): .
#42. Python 函式的可變物件預設引數陷阱
3.1K. 最近練習Python 的時候,遇到函式參數使用可變物件預設引數(例如list 或dict)的地雷,請各位想想下方這個簡單的函式範例: def foo(a=[]): ...
#43. 使用Python 來認識矩陣. 透過NumPy | by Yao-Jen Kuo - Pyradise
在使用Python 來認識向量一文中我們已經暸解怎麼在Python 中使用繪圖函數描繪向量、進行向量運算以及計算向量度量;這篇文章我們聚焦兩個維度的ndarray: ...
#44. [CodeIgniter 4] 設定全站共用變數| 文章 - DeTools 工具死神
用define 定義後,之後就能在任何的Controller、Library. ... 不過其實define 之後的會是常數才對就是了,如果要改變的話後面再用一個變數去接就好了 ...
#45. topic
370, 117, 易, B, Python語言中用來定義函數的關鍵字是, (A)return, (B)def ... 雙引號(“),來表示字串常數, (D)在三引號中之字串常數中,可加上換行、定位等特殊字元.
#46. Python From Scratch
辭典集(dictionary) 屬於Python 的映射物件(mapping object)。 example$ python ... 常數名稱. 功能說明. 傳回值範例. os.name. 傳回「作業系統環境名稱」字串.
#47. 如何使用Python 進行字串格式化 - TechBridge 技術共筆部落格
相對於Python 版本之後推薦使用的新式字串格式化,舊式版本使用 % 運算 ... 實際上Python 會把它變成字串常數和變數(過程中有優化) def hello(text, ...
#48. python怎么打自然常数e
引用:百度百科一段话; e,作为数学常数,是自然对数函数的底数。 ... 串)、Tuple(元组);可变数据(3 个):List(列表)、Dictionary(字典)、Set(集合)。
#49. GDScript 基礎— Godot Engine (latest) 正體中文(台灣) 文件
語法類似於Python (如區塊都是基於縮排來判斷,以及很多關鍵字都相同)。GDScript 的目標是要做一個 ... 常數名稱,若該常數包含腳本資源(若宣告 const MyScript ...
#50. 2019年10月16日星期三
2-2-1 常數識別字與宣告. 方法1 : #define 常數名稱常數值; 方法2 : const 資料型態常數名稱= 常 ... 用Python 做商管程式設計(一)第五周練習.
#51. Arduino 基本語法筆記
此外, 也可以用前置指令define 定義一個常數或巨集(運算式). ... 另外, 字串串接在Arduino C 可以像Python/Javacript 那樣用+ 直接串接, 在傳統C 語言 ...
#52. [C++ 文章收集] C++ 前置處理器
它的語法和C 完全不同,它也不需要了解C 語言的架構。它只是一種原始的文字編輯器. #define 敘述: #define 敘述可用來定義常數。
#53. APCS完全攻略: 從新手到高手, Python解題必備!
APCS完全攻略: 從新手到高手, Python解題必備! ... 而程式設計觀念題的考試重點包括:程式設計基本觀念、輸出入指令、資料型態、常數與變數、全域及區域、流程控制、迴 ...
#54. XOOPS佈景開發
... 完成的套件,支援多種語言如JavaScript, Python, HTML, Ruby, Python3, XML, HTML5, Perl, CSS, Node.js, PHP等等。 ... $smarty.const, 利用define函式定義的常數 ...
#55. Yahoo!奇摩購物中心--
本書的實作題以Python 語言來進行問題分析及程式實作。實作題的解答部份可分為四大架構:解題重點 ... 2-7 變數與常數 2-8-1 變數 2-8-2 常數 ... 2-10-2 #define 指令
#56. Lesson 2: 從函數計算與繪製開始 - 汪群超Chun-Chao Wang
Objective : 學習數學函數的計算方式數學函數的繪圖技術更熟練Python 的語法使用 ... String, List, Tuple, Dictionary, File 懂得定義矩陣: np.linspace(), ...
#57. 4.1 FAQ-286 如何定义全局函数,全局变量和常量?
如果您有经常在工作中使用的数学常数,则CONST.CNF文件是定义它们的好地方,因为它们在Origin运行时将始终可用。 如果Origin 正在运行,请退出程序。
#58. C 語言的宣告、定義、儲存類型(storage class) 與連結性 ...
宣告(declaration) 與定義(definition) ... 對於 static 存續期的變數, 在宣告時只能使用常數構成的運算式當成初值設定器, 不能使用到其他變數。
#59. SymPy 求解:如何在缺少常数项时得到零
SymPy 求解:如何在缺少常数项时得到零在本文中,我们将介绍如何使用SymPy库来求解方程中缺少常数项时得到零的方法。SymPy是一个用于进行符号数学计算的Python库, ...
#60. 常数· PHP/Python/前端/Linux 等等学习笔记
Python PHP Java 服务器前端软件MAC Window App端go Linux 此为php个人常用库 ... 要查找是否定义了常量,则可以简单地使用Php :: constant()或Php :: define().
#61. Python 入門特訓- 基礎實作到證照攻略- 線上教學課程
Python 證照攻略課程,教你Python 的程式語法與Python 證照攻略, ... 基本程式設計(一)| 變數與常數_1 ... 詞典(Dictionary)| 詞典的建立、運作及應用.
#62. 考科1:資料導向程式設計-參考樣題
使用Python 語言處理資料時,下列何者函式可用於取得物件之長度整. 數數值? ... 料型態的常數、變數或運算式 ... (B) 資料定義語言(Data Definition Language, DDL).
#63. Re: [問題] None在def中的變化- 看板Python - 批踢踢實業坊
這個**問題**其實被蠻多人討論過了,但比起爭論它是否是Python 的設計瑕疵 ... 接著執行腳本的第8 行,就是很單純的載入兩個函數`print`, `f` 和常數 ...
#64. cpython解释器源码剖析(一)——字节码 - I'm dev
Python 也有编译器一般的观点中常将编程语言分为编译执行和解释执行两类 ... 位置对应了常数 3 ,通过直接对字节码对象反汇编(而不是像之前只对字节码 ...
#65. arXiv:1911.12338v2 [cs.OH] 8 Oct 2020
Python 2 was originally designed around 3-way comparison operators, defined via the __cmp__ method [3]. How- ever, 2-way “rich comparison” ...
#66. SciPy Constants
... and easy to understand Web building tutorials with lots of examples of how to use HTML, CSS, JavaScript, SQL, PHP, Python, Bootstrap, Java and XML.
#67. 用预处理指令#define 声明一个常数,用以表明1年中有多少 ...
怎样将字符串中第一个字母大写? 3 回答. 282 阅读. 在Python中如何生成一个随机数? 1 ...
#68. Stanford Code in Place - Python入門課程Week 7
介紹基於Stanford CS106A Programming Methodologies的免費Python程式線上課程, ... ("W")、KEY_MEN ("M") 兩個常數,代表nested dictionary 內的key.
#69. C++:宏定义(#define)和常量(const)的区别
多角度分析. 角度1: 就定义常量说的话, const 定义的常数是变量也带类型, #define 定义的只是 ...
#70. 对于结构体指针+、-常数的理解(page_to_pfn和pfn_to_page)
对于结构体指针+、-常数的理解(page_to_pfn和pfn_to_page) ... #define page_to_pfn(page) (((page) - mem_map) + PHYS_PFN_OFFSET)
#71. [PERL] 02-註解、變數和常數
perl的註解、變數和常數. ... 分享連結 [PERL] 02-註解、變數和常數@新精讚 ... 使用defined()來判斷是否為空值,請看03-PERL條件式判斷的if範例 ...
#72. ZeroJudge - f427: 高雄市109年資訊競賽國中組第五題解題心得
... Python 的math.pi 等等。不過Visual Studio 寫C++ 要使用cmath 所定義的常數前,要在<cmath> 前面加上「#define _USE_MATH_DEFINES」才能使用。
#73. 第二章常數與變數宣告變數(variable)
例. 如"#define SIZE 512" 就會使程式中的SIZE. (請注意大小寫), 在編譯之前就先代換成. 512 。 8. 定義常數(Symbolic Constant). • 所以如果程式中有如下敘述:.
#74. C語言- 第三十三章| struct - 位元欄位| J.J.'s Blogs
除了使用前述的常數宣告方法外,我們還可以使用「 define」這個前置處理器 ... 相關文章處理Python 拋出的Exception 例外異常並使用Assert 確保程式正常執行Python 中 ...
#75. Min Stack
#76. 再谈C语言位域| Tony Bai
Python MicroPython. ... 除了使用前述的常數宣告方法外,我們還可以使用「 define」這個前置處理器指令(Preprocessor Directive) 8 來定義常數,其語法如下: 常數 ...
#77. Atmel Studio 7.0延时参数问题- Microchip MCU - 电子工程世界
include #define F_CPU 8000000UL#include double a=500;int main(void){ ... 为了得到最好的效率和精度,使用了汇编做延时,就只能使用常数。
#78. Java程序员面试笔试宝典 - Google 圖書結果
4)用C语言预处理指令#define声明一个常数,用以表示一年有多少秒(假设一年有365天)____。 ... 下列哪一组对应关系与此类似,请做出解释 1PHP,Python;2JSP,servlet;3java ...
#79. Haskell 随笔二λ演算
python (lambda x,y: x + y)(1, 2). js ((x, y) => x + y)(1, 2) ... 由于常数在Haskell中是字面量,所以这里就用英文来代替阿拉伯数字.
#80. Arduino 程式庫3csxit
Python 程式設計賺錢的管錢的花錢的. Arduino 函式庫下載. ... Const以名稱來定義一個常數的指令與define 功能類似不過. 指定驅動程式安裝路徑在arduino資料夾 ...
#81. Telemac 2d
向telemac-mascaet用户社区免费提供常用谐波常数数据库。 ... In addition, we define the path to a directory where the TELEMAC-2D runs shall be conducted ...
python define常數 在 Re: [問題] None在def中的變化- 看板Python - 批踢踢實業坊 的推薦與評價
這個**問題**其實被蠻多人討論過了,但比起爭論它是否是 Python 的設計瑕疵,我認為
深入了解其背後的運作原理是更值得做的事情。
這邊我先以原 PO 所問的這個段內容起頭:
> ... 想請問為什麼 ex2 裡引述預設值改為 None 時,不會發生印出的內容包含前一次
> 呼叫內容,第一次輸出['a']後,result不是已經變成['a']了嗎 ...
簡單版的回答是:
在那個 function 內所用到的 `result` 一開始指向的是 `None`,如同 signature
裡的 `result=None` 所表示。所以在執行 `if result is None:` 時,所得到的結
果是 True ,因此 `result` 就如下一行一樣,被重新指向一個新的 empty list。
(延伸閱讀: name binding, https://nedbatchelder.com/text/names.html )
詳細版的回答:
一開始, `result` 指向的物件是存在 local scope 裡的。而因為該物件是 `None`
,所以 `result` 也就是指向 `None`。而要知道一開始 local scope 內有什麼東西
,你可以在 if 的上一行加上 `print(locals())` 來觀察。
為求接下來撰文方便,我們用官方文件中的例子來說明,請參考下圖:
程式執行到 `print(f(1))` 時,`print` 裡的 function call `f(1)` 會先被執
行。而因為 `f` 第一個參數 `a` 所拿到的值是 1,而 `L` 沒有被指定,所以進入
function 後,`locals()` 回傳的內容會是 `{'a': 1, 'L': []}`。
在執行 `L.append(a)` 之後,`L` 這個 list 的內容變成了 `[1]`。但是,記得
前面提到的 name binding 嗎?由於 `L` 指向的正是 local scope 的那個 `L`,
所以如果接著再呼叫一次 `locals()`,回傳的內容會是 `{'a': 1, 'L': [1]}`。
因此執行到 `print(f(2))` 時,由於稍早在 `f` 的 local scope 內的 `L` 已經
被改變了,所以這時候 `print(locals())` 裡看到的 `L` 就是已經被改變的狀態。
(不過使用 `locals()` 來觀察一個 function 被執行時其 local scope 的內容並
不完全適合,**詳細的原因後續會再說明**。)
但是這跟 mutable/immutable object 有關係嗎?以這個例子來說其實不太適合,
讓我們將它稍微改寫成以下兩個版本:
- mutable default argument
- immutable default argument
這樣一來,就可以很明顯地了解這個問題跟使用 mutable/immutable object 作為
預設值的差別了。
然而,我們知道了 `locals()` 的用處,那是否可以用它來做些有趣的事情呢?
譬如,直接使用 `locals()` 去修改預設值(暫不考慮有傳入 `L` 的情況)?
很抱歉,失敗了。原因有點複雜,恕我在此省略。但其實這點在官方文件也有提到
> Note: The contents of this dictionary should not be modified;
> changes may not affect the values of local and free variables used
> by the interpreter.
https://docs.python.org/3/library/functions.html#locals
但有沒有其他辦法去達到這個目的呢?其實還是有的,方法如下
不過很明顯地,比起這麼做,倒不如用常見的方法:將預設值設為 `None`,然後在函
數內用 if 判斷以重新設定。
稍微扯遠了。這個問題的根本還是需要回到 "參數初始化的方式" 來討論。原因也如同
官方文件所提到的
> ... The default value is evaluated only once ...
https://docs.python.org/3/tutorial/controlflow.html#more-on-defining-functions
還有,如果 `result` 從一開始就沒有被定義在 function signature 裡的話,在
local scope 內就不會 `result`。在這種情況下,便會循著所謂的 LEGB rule (
local, enclosed, global, built-in) 去做 name resolution 。
(延伸閱讀: LEGB rule,
https://sebastianraschka.com/Articles/2014_python_scope_and_namespaces.html )
----------------------------------------------------------------------------
但光是這樣的回答,相信還無法滿足熱血的版眾。其中官方文件的那句
> ... The default value is evaluated only once ...
沒有被解答的話,一定讓人覺得心癢癢的。
## 深入又不太深入地檢視 CPython 的執行過程
要了解官方文件的那句話,我們得先了解 Python 是如何執行一個腳本的。為求單純,
接下來皆以 CPython 3.7 為 runtime 來說明。
在我們下指令 `$ python your_script.py` 以執行腳本時,其實在背後會先透過一個
compiler 將腳本 compile 成 .pyc 檔,之後再交由 interpreter 去執行。這部分
我描述地非常簡略,有興趣的人可以參考 Louie Lu 所寫的
"Python 底層運作 01 – 虛擬機器與 Byte Code"
這篇文章。而我要談到的部分為該文章所附圖中的 virtual machine 這塊,恕我偷懶
直接貼上該圖
https://blog.louie.lu/wp-content/uploads/2017/04/python_flow.png
其中, code object 可以約略地視為一個個程式碼區塊 (e.g. module, function)
被編譯後產生的物件,其帶有執行時需要的資料;而 bytecode 則是讓 interpreter
執行的步驟表。因此,要了解 CPython 的執行過程,我們可以從 bytecode 下手。
以前面提到的例子來說,我們可以使用 `dis` 這個 module 來分析:
上圖中左側兩種顏色區塊分別對應到右側由 `dis.dis()` 印出的 bytecode 。而
bytecode table 中各個欄位代表的東西如下:
由於 CPython 的 interpreter 是一個 stack-based virtual machine,所以上面
看到的 opname 都是用來操作 stack 的指令。不過這邊就先暫時不一一介紹各個
opname 所代表的意思,我們直接跟著左側原始碼來看:
1. 進入 `main()` 後,開頭的第 4 行就是一個函數的定義。而對照到 bytecode
table ,可以看到有著一連串的指令等著要執行。其中 `4 LOAD_CONST` 和
`6 LOAD_CONST` 分別是載入一個 code object 和函數 `f` 的名稱。
而接著的 `8 MAKE_FUNCTION` 正是一個用來建立 function object 的指令。
最後 `10 STORE_FAST` 則是將上一步驟所產生的 function object 以 `f` 為
名稱儲存在某處。
2. 接著執行腳本的第 8 行,就是很單純的載入兩個函數 `print`, `f` 和常數參數
`1`,然後再先後呼叫 `f` 和 `print` 兩個函數。
3. 執行腳本的第 9 行,同上。不過因為這行是 `main()` 的最後一行,所以在最後
還會看到兩行指令 `36 LOAD_CONST`, `38 RETURN_VALUE`,這也就是我們熟悉的
"沒有明確透過 `return` 回傳結果的話,預設會回傳 `None`"的設計。
而根據上述第 1 點,我們可以知道一個 function object 的建立時間點就是執行到
`def func():` 的時候。但是 `MAKE_FUNCTION` 到底幫我們處理了什麼,我們得從
CPython 的原始碼來了解。
在 Python 3.7 中, bytecode 裡的各種 instruction 是由 ceval.c 這個檔案裡的
`_PyEval_EvalFrameDefault` 這個函數來處理的,其可以簡單視為由 for + switch
所構成的 bytecode dispatcher 。
話題回到我們所感興趣的 `MAKE_FUNCTION`,其中前三行為:
```c
// ceval.c::_PyEval_EvalFrameDefault
// https://github.com/python/cpython/blob/cb75801/Python/ceval.c#L3201-L3207
PyObject *qualname = POP(); // 1
PyObject *codeobj = POP(); // 2
PyFunctionObject *func = (PyFunctionObject *)
PyFunction_NewWithQualName(codeobj, f->f_globals, qualname); // 3
```
1. 取出 stack 最上層的物件,其為稍後建立的 func 的 `__qualname__`
2. 取出 stack 最上層的物件,其為稍後建立的 func 的 `__code__`
3. 透過 `PyFunction_NewWithQualName` 建立 function object `func`
到這邊為止,我們可以知道這就是單純地在建立一個 function object,尚未處理其他
如 keyword argument, closure ... 等。所以對於一個簡單的函數如:
```python
def foo(): return
```
藉著這三行就已經處理完了。
而同樣在 `MAKE_FUNCTION` 這個區塊裡,後半部還有以下的處理:
```c
// ceval.c::_PyEval_EvalFrameDefault
// https://github.com/python/cpython/blob/cb75801/Python/ceval.c#L3215-L3230
if (oparg & 0x08) {
assert(PyTuple_CheckExact(TOP()));
func ->func_closure = POP(); // 1
}
if (oparg & 0x04) {
assert(PyDict_CheckExact(TOP()));
func->func_annotations = POP(); // 2
}
if (oparg & 0x02) {
assert(PyDict_CheckExact(TOP()));
func->func_kwdefaults = POP(); // 3
}
if (oparg & 0x01) {
assert(PyTuple_CheckExact(TOP()));
func->func_defaults = POP(); // 4
}
```
1. 設定 `func_closure`,也就是在 Python 中的 `func.__closure__`
2. 設定 `func_annotations`,也就是在 Python 中的 `func.__annotations__`
3. 設定 `func_kwdefaults`,也就是在 Python 中的 `func.__kwdefaults__`
4. 設定 `func_defaults`,也就是在 Python 中的 `func.__defaults__`
好的,看到這邊是否有想起我們在前面有做了一件調皮的事情?我們那時直接修改了
`func.__defaults__` 來重設 `L` 的預設值以模擬每次呼叫 `f(x)` 時使用的 `L`
都是一個 empty list 。但是這邊明明只看到
```c
func->func_defaults = POP();
```
這樣的一行,很明顯地只是在賦值而已,跟文件上說的 "The default value is
evaluated only once" 應該不是同一件事情。
沒錯,因為 evaluation 在執行 `MAKE_FUNCTION` 前就已經發生了。我們回想一下
那一段 bytecode
可以發現,其實 `L` 的預設值早在 `0 BUILD_LIST 0` 就已經處理掉了。
(`2 BUILD_TUPLE 1` 在做的事情則是將上一步建立出的 list 包成 tuple ,因為
`func.__defaults__` 接受的型別是 tuple)
我們將那個範例稍微改寫一下,應該就可以更容易理解:
這裡我們將 `L` 的預設值改為 `make_empty_list()`,在右側 bytecode table 中
也可以看到 `make_empty_list()` 確實也只有在 `MAKE_FUNCTION` 前被執行一次,
因此這個版本我們預期的結果也如同 `def f(a, L=[]):` 一樣,如下圖
以上,這就是文件中 "The default value is evaluated only once" 的意思。
如果看到這邊還覺得不過癮,想要更深入理解每個 bytecode instruction 在做什麼
事情的話,可以試著玩玩看我最近在做的一個專案 `bytefall`。這個專案主要是延伸
自 nedbat/byterun 和 darius/tailbiter ,而我將它改寫成支援 Python 3.4 ~
3.8 並加入一些特殊的功能,如下圖所示的 opcode tracer
repo: https://github.com/naleraphael/bytefall
repl.it (線上試玩): https://repl.it/@naleraphael/pymutabledefaults
## 補充
在對 CPython virtual machine 有了稍微地了解後,我們來談談為何前面提到 "用
`locals()` 來觀察一個 function 被執行時其 local scope 的內容並不完全適合"
這件事得先從 `locals()` 的實作講起:
```c
// bltinmodule.c::builtin_locals_impl
//
https://github.com/python/cpython/blob/681044a/Python/bltinmodule.c#L1604-L1613
static PyObject *
builtin_locals_impl(PyObject *module)
{
// ... omitted
d = PyEval_GetLocals();
// ... omitted
}
```
這邊可以看到,當我們呼叫 `locals()` 時,實際上會呼叫到 `PyEval_GetLocals()`
。而 `PyEval_GetLocals()` 的實作如下
```c
// ceval.c::PyEval_GetLocals
// https://github.com/python/cpython/blob/3.7/Python/ceval.c#L4436-L4450
PyObject *
PyEval_GetLocals(void)
{
// ... omitted
return current_frame->f_locals;
}
```
它回傳的其實只是當前 frame 的 `f_locals`。
你可以先把 frame 當作是一個帶有 scope 中各種資訊的物件,也就是說,當你進入了
一個新的 scope ,也就意味著 interpreter 正在處理那個 frame 中的資訊。這是否
讓你想起前面提到的 code object?沒錯,每個 frame 都帶有一個正要被執行的 code
object (`f_code`)。而 `f_locals` 就是那個 scope 裡的 local objects 。
但這還沒解釋我們的疑問。我們再回想一下前面提到的,執行 `print(f(1))` 時的
bytecode instruction 也就是下圖中右側黃色區塊
其中的 `14 LOAD_FAST 0 (f)` 看起來是要從目前這個 frame 取得 `f` 這個
函數,但是它背後是如何處理的呢?
```c
// ceval.c::_PyEval_EvalFrameDefault
// https://github.com/python/cpython/blob/681044a/Python/ceval.c#L1074-L1085
{
PyObject *value = GETLOCAL(oparg);
// ... omitted
}
```
這個 macro `GETLOCAL` 的定義為
```c
// ceval.c::_PyEval_EvalFrameDefault
// https://github.com/python/cpython/blob/681044a/Python/ceval.c#L792
#define GETLOCAL(i) (fastlocals[i])
// ceval.c::_PyEval_EvalFrameDefault
// https://github.com/python/cpython/blob/681044a/Python/ceval.c#L880
fastlocals = f->f_localsplus;
```
這邊我們發現實際上 `LOAD_FAST` 是從 `f->f_localsplus` 尋找資料,而非
`f->f_locals`。而 `f_localsplus` 的定義如下:
```c
// frameobject.h
// https://github.com/python/cpython/blob/681044a/Include/frameobject.h#L46
PyObject *f_localsplus[1]; /* locals+stack, dynamically sized */
```
由此可知,`locals()` 回傳的東西並不包含當時 interpreter stack 裡的資料,
但是這又和進入一個 function scope 有什麼關係呢?以下我直接列出執行順序
```
// starts from `CALL_FUNCTION`
// https://github.com/python/cpython/blob/681044a0/Python/ceval.c#L3121-L3131
ceval.c::call_function =>
call.c::_PyFunction_FastCallKeywords =>
(1) call.c::function_code_fastcall =>
ceval.c::PyEval_EvalFrameEx => (tstate->interp->eval_frame =>)
_PyEval_EvalFrameDefault
(2) ceval.c::_PyEval_EvalCodeWithName =>
ceval.c::PyEval_EvalFrameEx =>
_PyEval_EvalFrameDefault
```
我們可以發現在執行 `CALL_FUNCTION` 之後,會再透過 interpreter 去處理一個
新的 frame 。這也呼應到前面講的 "當你進入了一個新的 scope ,也就意味著
interpreter 正在處理那個 frame 中的資訊"。
## 回到原主題
關於 mutable default argument ,除了去探討為何 Python 為何會這樣設計,如:
https://stackoverflow.com/questions/1132941/
https://softwareengineering.stackexchange.com/questions/157373
也可以在深入了解這樣的設計後,想想看可以透過它達成哪些功能,例如下面文章說的
caching, local rebinding
https://effbot.org/zone/default-values.htm#valid-uses-for-mutable-defaults
類似的技巧其實在官方文件也有提到:
https://docs.python.org/3/faq/programming.html#why-are-default-values-shared-between-objects
要評論這個特性的好壞,個人認為一切取決於你是如何使用它的。如果你是一個負責
管理一個團隊的開發,又覺得這個特性很容易造成問題,你也可以透過 pre-commit
hook + pylint 來處理 (dangerous-default-value (W0102))。
當然,甚至可以投入 Python 社群為 source code 貢獻:
https://mail.python.org/pipermail/python-ideas/2007-January/000121.html
最後再推薦一篇由 Anthony Shaw 所寫的好文:
https://realpython.com/cpython-source-code-guide/
裡面也提到了(在說明 symbol tables 那部分的下方)
> If you’ve ever wondered why Python’s default arguments are mutable,
> the reason is in this function. You can see they are a pointer to the
> variable in the symtable. No extra work is done to copy any values to
> an immutable type.
## Bonus
也許你沒料到 Guido 對這個特性的想法
https://twitter.com/gvanrossum/status/1014524798850875393
--
※ 發信站: 批踢踢實業坊(ptt.cc), 來自: 220.136.19.245 (臺灣)
※ 文章網址: https://www.ptt.cc/bbs/Python/M.1585988949.A.A54.html
... <看更多>